
// ================================================================================================
// -*- C++ -*-
// File: WorldState.cpp
// Author: Guilherme R. Lampert
// Created on: 16/09/13
// Brief: State of the Ants world this turn.
// ================================================================================================

#include "WorldState.hpp"

WorldState::WorldState(const StartupParameters * startupParams) :
	gameOver(false),
	turnNum(1),
	numPlayers(1),
	params(startupParams),
	map(NULL),
	searchGraph(/* digraph = */ false),
	searchAlgorithm(searchGraph)
{
	DBG_ASSERT(params != NULL);

	// Dump startup params:
	LOG("======== Startup parameters for WorldState ========");
	LOG("playerSeed...........: " << params->playerSeed);
	LOG("numTurns.............: " << params->numTurns);
	LOG("mapRows..............: " << params->mapRows);
	LOG("mapCols..............: " << params->mapCols);
	LOG("loadTime.............: " << params->loadTime);
	LOG("turnTime.............: " << params->turnTime);
	LOG("viewRadiusSquared....: " << params->viewRadiusSquared);
	LOG("viewRadius...........: " << params->viewRadius);
	LOG("attackRadiusSquared..: " << params->attackRadiusSquared);
	LOG("attackRadius.........: " << params->attackRadius);
	LOG("spawnRadiusSquared...: " << params->spawnRadiusSquared);
	LOG("spawnRadius..........: " << params->spawnRadius);
	LOG("===================================================");

	DBG_ASSERT(params->mapRows >= 1);
	DBG_ASSERT(params->mapCols >= 1);

	// Allocate map:
	const size_t totalCells = (params->mapCols * params->mapRows);

	LOG("Allocating " << totalCells << " cells for game map...");
	LOG("sizeof(MapCell) = " << sizeof(MapCell) << " bytes");
	LOG("~ memory used for map cells: " << (sizeof(MapCell) * totalCells) << " bytes");

	map = new MapCell[totalCells]; // Allocate
	memset(map, 0, (sizeof(MapCell) * totalCells)); // Zero fill

	// Important: set ant shared data:
	Ant::viewDistance = static_cast<int>(floor(params->viewRadius));
	LOG("Ant view distance: " << Ant::viewDistance);

	// Prepare search space:
	SetupSearchGraph();

	// Indicate to the host that we got
	// the initial parameters and are ready to go!
	EndTurn();
}

WorldState::~WorldState()
{
	// Delete map:
	delete[] map;
	map = NULL;
}

void WorldState::Update()
{
	#if defined (ANTS_DEBUG)
	DebugDump(); // Dump map if debug
	#endif // ANTS_DEBUG

	// Make our moves:
	MakeMoves();

	// End this turn:
	EndTurn();
}

bool WorldState::GameOver() const
{
	return (gameOver);
}

MapCell & WorldState::MapCellAt(int x, int y) const
{
	DBG_ASSERT(map != NULL);
	DBG_ASSERT(x >= 0);
	DBG_ASSERT(x < MaxMapX());
	DBG_ASSERT(y >= 0);
	DBG_ASSERT(y < MaxMapY());
	return (map[x + y * params->mapCols]);
}

std::istream & operator >> (std::istream & is, WorldState & state)
{
	std::string inputType, junk;

	// Find out which turn it is:
	while (is >> inputType)
	{
		if (inputType == "end")
		{
			state.gameOver = true;
			break;
		}
		else if (inputType == "turn")
		{
			is >> state.turnNum;
			break;
		}
		else
		{
			std::getline(is, junk);
			LOG("Got junk line: \"" << junk.c_str() << "\"");
		}
	}

	if (state.turnNum == 0)
	{
		LOG("WARNING: Got turn zero after startup!");
		// Turn zero should be received only once, by AntCommander.
		return (is);
	}

	int row = -1, col = -1, player = -1;

	// Read information about the current turn:
	while (is >> inputType)
	{
		if (inputType == "w") // Water cell:
		{
			is >> row >> col;
			MapCell & cell = state.MapCellAt(col, row);
			CellSetContents(cell, CC_WATER);
			CellSetPlayer(cell, -1);
		}
		else if (inputType == "f") // Food cell:
		{
			is >> row >> col;
			MapCell & cell = state.MapCellAt(col, row);
			CellSetContents(cell, CC_FOOD);
			CellSetPlayer(cell, -1);
		}
		else if (inputType == "a") // Alive ant:
		{
			is >> row >> col >> player;

			MapCell & cell = state.MapCellAt(col, row);
			CellSetContents(cell, CC_ANT);
			CellSetPlayer(cell, player);

			// player == 0 its me;
			// else some other player.
			if (player == 0)
			{
				// Update our ant list:
				state.UpdateAliveAnt(col, row);
			}
		}
		else if (inputType == "d") // Dead ant:
		{
			is >> row >> col >> player;

			MapCell & cell = state.MapCellAt(col, row);
			CellSetContents(cell, CC_DEAD_ANT);
			CellSetPlayer(cell, player);

			// player == 0 its me;
			// else some other player.
			if (player == 0)
			{
				// Update our ant list:
				state.UpdateDeadAnt(col, row);
			}
		}
		else if (inputType == "h") // Hill cell
		{
			is >> row >> col >> player;
			// Ant "hill" and which player it belongs to.
			MapCell & cell = state.MapCellAt(col, row);
			CellSetContents(cell, CC_ANT_HILL);
			CellSetPlayer(cell, player);
		}
		else if (inputType == "players") // Number of players still in the game:
		{
			is >> state.numPlayers;
			LOG("Game has " << state.numPlayers << " active players");
		}
		else if (inputType == "scores") // Score information:
		{
			// Score list ignore for now...
			double score = 0;
			for (int p = 0; p < state.numPlayers; ++p)
			{
				is >> score;
			}
		}
		else if (inputType == "go") // End of turn input:
		{
			if (state.gameOver)
			{
				is.setstate(std::ios::failbit);
			}
			break;
		}
		else
		{
			std::getline(is, junk);
			LOG("Got junk line: \"" << junk.c_str() << "\"");
		}

		inputType.clear();
	}

	return (is);
}

void WorldState::MakeMoves()
{
	AntList::iterator it = myAnts.begin();
	AntList::iterator end = myAnts.end();

	while (it != end)
	{
		Ant & ant = (*it);

		if (ant.HasGoal())
		{
			// Keep moving to goal:
			ant.MoveToGoal();
		}
		else
		{
			Location location;
			MapCellContents found;
			static const MapCellContents lookFor[3] = { CC_FOOD, CC_ANT_HILL, CC_ANT };

			// Find new goal:
			if (ant.HasLineOfSightTo(lookFor, 3, location, found))
			{
				ant.SetGoal(location.x, location.y, found);
			}
			else
			{
				if (ant.ShouldDoComplexSearch())
				{
					// Set search source (current ant location):
					const Location & antLocation = ant.GetLocation();
					searchAlgorithm.SetSourceNode(antLocation.x + antLocation.y * MaxMapX());

					// Not particular target node is set. Stop when an interest item is found
					// or when the whole search space has been searched.
					searchAlgorithm.SetTargetNode(-1);
					const bool foundItem = searchAlgorithm.RunSearch();

					if (foundItem)
					{
						const int target = searchAlgorithm.GetTargetNode();
						DBG_ASSERT(target != -1);

						const MyGraph::NodeType & node = searchGraph.GetNode(target);
						std::list<int> path = searchAlgorithm.GetPathToTarget();

						DBG_ASSERT(node.cell != NULL);
						ant.SetGoal(path, static_cast<MapCellContents>(node.cell->contents));
					}
				}
				else
				{
					ant.RandomMove();
				}
			}
		}

		#if defined (ANTS_DEBUG)
		ant.DebugDump();
		#endif // ANTS_DEBUG

		++it;
	}
}

void WorldState::EndTurn()
{
	// Clear mutable stuff, but keep water and hill cells:
	const size_t totalCells = (params->mapCols * params->mapRows);
	for (size_t i = 0; i < totalCells; ++i)
	{
		if (CellIsWater(map[i]) || CellIsAntHill(map[i]))
		{
			continue;
		}

		CellClear(map[i]);
	}

	++turnNum;
	std::cout << "go" << std::endl;
}

void WorldState::UpdateAliveAnt(int x, int y)
{
	AntList::iterator it = myAnts.begin();
	AntList::iterator end = myAnts.end();

	while (it != end)
	{
		if ((*it).IsAt(x, y))
		{
			// We already have this ant
			return;
		}
		++it;
	}

	// New ant!
	myAnts.push_back(Ant(x, y, this));
	LOG("Spawned new ant at (" << x << "," << y << ")");
}

void WorldState::UpdateDeadAnt(int x, int y)
{
	AntList::iterator it = myAnts.begin();
	AntList::iterator end = myAnts.end();

	while (it != end)
	{
		if ((*it).IsAt(x, y))
		{
			// Found it, remove from list:
			myAnts.erase(it);
			return;
		}
		++it;
	}
}

void WorldState::SetupSearchGraph()
{
	// Every map cell becomes a node of the graph:
	for (int y = 0; y < params->mapRows; ++y)
	{
		for (int x = 0; x < params->mapCols; ++x)
		{
			MapCell & cell = MapCellAt(x, y);
			const Location nodeLocation = {{x, y}};
			const int nodeIndex = searchGraph.GetNextFreeNodeIndex();

			searchGraph.AddNode(GraphNode(nodeIndex, nodeLocation, &cell));
		}
	}

	LOG("Search graph has " << searchGraph.NumNodes() << " nodes");
	LOG("~ memory used for graph nodes: " << (searchGraph.NumNodes() * sizeof(GraphNode)) << " bytes");

	// Build set of edges connecting every node to its 4 immediate neighbors:
	BuildEdges();

	// Allocate memory needed for the search algorithm.
	// The graph never changes after creation.
	searchAlgorithm.SetupSearch();
}

void WorldState::BuildEdges()
{
	// Create graph edges. Every node connects with its 4 adjacent neighbors.
	// Nodes in the diagonal corners are not accessible from a given node.
	// Note that the map wraps around, that is, from node x=0 you can reach node x=width-1,
	// from x=width-1 can reach zero. Same goes for y.
	const int width  = MaxMapX();
	const int height = MaxMapY();
	int tmp;

	for (int y = 0; y < params->mapRows; ++y)
	{
		for (int x = 0; x < params->mapCols; ++x)
		{
			const int node = (x + y * width);

			// Right and left neighbors:
			tmp = ((x + 1) < width) ? (x + 1) : 0;
			const int rightNeighbor = (tmp + y * width);
			searchGraph.AddEdge(GraphEdge(node, rightNeighbor));

			tmp = ((x - 1) >= 0) ? (x - 1) : (width - 1);
			const int leftNeighbor = (tmp + y * width);
			searchGraph.AddEdge(GraphEdge(node, leftNeighbor));

			// Down and up neighbors:
			tmp = ((y + 1) < height) ? (y + 1) : 0;
			const int downNeighbor = (x + tmp * width);
			searchGraph.AddEdge(GraphEdge(node, downNeighbor));

			tmp = ((y - 1) >= 0) ? (y - 1) : (height - 1);
			const int upNeighbor = (x + tmp * width);
			searchGraph.AddEdge(GraphEdge(node, upNeighbor));
		}
	}

	LOG("Search graph has " << searchGraph.NumEdges() << " edges");
}

void WorldState::DebugDump() const
{
#if defined (ANTS_DEBUG)

	DebugLog & debug = DebugLog::Instance();

	debug << "===================================================" << std::endl;
	debug << "World map after input: (turn = " << turnNum << ")" << std::endl;

	for (int y = 0; y < params->mapRows; ++y)
	{
		for (int x = 0; x < params->mapCols; ++x)
		{
			const MapCell & cell = MapCellAt(x, y);

			if (CellIsWater(cell))
			{
				debug << '%';
			}
			else if (CellIsFood(cell))
			{
				debug << '*';
			}
			else if (CellIsAntHill(cell))
			{
				debug << (char)('A' + cell.player);
			}
			else if (CellIsAnt(cell)) // By ant we read ALIVE ant!
			{
				debug << (char)('a' + cell.player);
			}
			else if (CellIsVisible(cell))
			{
				debug << '.';
			}
			else
			{
				debug << '?'; // Unknown map cell
			}
		}
		debug << std::endl;
	}

	LOG("Ants we have: " << myAnts.size());

#endif // ANTS_DEBUG
}
